package edu.ccut.saturn.studio.server.tomcat;

import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.StringTokenizer;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;


/**
 * Start and stop Tomcat
 * Subclasses contains all information specific to a Tomcat Version
 */

public abstract class TomcatBootstrap {

	private static final int RUN = 1;
	private static final int LOG = 2;	
	private static final int ADD_LAUNCH = 3;
	
	public abstract String[] getClasspath();
	public abstract String[] getVmArgs();
	public abstract String[] getPrgArgs(String command);
	public abstract String getStartCommand();
	public abstract String getStopCommand();
	public abstract String getMainClass();

	abstract public String getLabel();

	abstract public String getContextWorkDir(String workFolder);

	abstract public IPath getServletJarPath();
	abstract public IPath getJasperJarPath();
	abstract public IPath getJSPJarPath();

	
	/**
	 * Return the tag that will be used to find where context definition should be added in server.xml
	 */
	public abstract String getXMLTagAfterContextDefinition();

	/**
	 * See %TOMCAT_HOME%/bin/startup.bat
	 */
	public void start() throws CoreException {
		this.runTomcatBootsrap(getStartCommand(), true, RUN, true);
	}
	/**
	 * See %TOMCAT_HOME%/bin/shutdown.bat
	 */
	public void stop() throws CoreException {
		this.runTomcatBootsrap(getStopCommand(), false, RUN, false);
	}

	/**
	 * Simply stop and start
	 */
	public void restart() throws CoreException {
		this.stop();

		// Hack, need more testings
		try {
			Thread.sleep(5000);
		} catch (InterruptedException ex) {
		}

		this.start();
	}

	/**
	 * Write tomcat launch configuration to .metadata/.log
	 */
	public void logConfig() throws CoreException {
		this.runTomcatBootsrap(getStartCommand(), true, LOG, false);
	}
	
	/**
	 * Create an Eclipse launch configuration
	 */
	public void addLaunch() throws CoreException {
		this.runTomcatBootsrap(getStartCommand(), true, ADD_LAUNCH, true);
	}
	
	/**
	 * Launch a new JVM running Tomcat Main class
	 * Set classpath, bootclasspath and environment variable
	 */
	private void runTomcatBootsrap(String tomcatBootOption, boolean showInDebugger, int action, boolean saveConfig) throws CoreException {
		String[] prgArgs = this.getPrgArgs(tomcatBootOption);

		String[] classpath = new String[0];
		classpath = addPreferenceJvmToClasspath(classpath);
		classpath = concatUniq(classpath, this.getClasspath());

		String[] vmArgs = this.getVmArgs();
		String[] bootClasspath = addPreferenceJvmToBootClasspath(new String[0]);

		StringBuffer programArguments = new StringBuffer();
		for (int i = 0; i < prgArgs.length; i++) {
			programArguments.append(" " + prgArgs[i]);
		}

		StringBuffer jvmArguments = new StringBuffer();
		for (int i = 0; i < vmArgs.length; i++) {
			jvmArguments.append(" " + vmArgs[i]);
		}

		if(action == RUN) {
			VMLauncherUtility.runVM(getLabel(), getMainClass(), classpath, bootClasspath, jvmArguments.toString(), programArguments.toString(), isDebugMode(), showInDebugger, saveConfig);
		}
		if(action == LOG) {
			VMLauncherUtility.log(getLabel(), getMainClass(), classpath, bootClasspath, jvmArguments.toString(), programArguments.toString(), isDebugMode(), showInDebugger);
		}
		if(action == ADD_LAUNCH) {
			VMLauncherUtility.createConfig(getLabel(), getMainClass(), classpath, bootClasspath, jvmArguments.toString(), programArguments.toString(), isDebugMode(), showInDebugger, true);
		}		

	}


	private boolean isDebugMode() {
		return TomcatLauncherPlugin.isDebugMode();
	}

	protected String getTomcatDir() {
		return TomcatLauncherPlugin.getTomcatDir();
	}

	protected String getTomcatBase() {
		return TomcatLauncherPlugin.getTomcatBase();
	}

	/**
	 * Concat two arrays of Strings, and prevent that duplicate
	 * Strings are present in result
	 * part2 is appended to part1
	 */
	
	@SuppressWarnings("unchecked")
	public static String[] concatUniq(String[] part1, String[] part2) {
		ArrayList nlist = new ArrayList();

		for (int i = 0; i < part1.length; i++) {
			if (! nlist.contains(part1[i]))
				nlist.add(part1[i]);
		}
        
		for (int i = 0; i < part2.length; i++) {
			if (! nlist.contains(part2[i]))
				nlist.add(part2[i]);
		}
        
		return (String[])nlist.toArray(new String[0]);      
	}

	/**
	 * See StringTokenizer for delim parameter format 
	 */

	@SuppressWarnings({ "unchecked", "deprecation" })
	public static String[] cutString(String str, String delim) {
		ArrayList strings = new ArrayList();
		StringTokenizer tokenizer = new StringTokenizer(str, delim);
		while (tokenizer.hasMoreTokens()) {
			strings.add( URLDecoder.decode( tokenizer.nextToken()) );	
		}
		
		return (String[])strings.toArray(new String[0]);
	}
	
	private String[] addPreferenceJvmToClasspath(String[] previous) {
		String[] prefClasspath = cutString("", ";");
		return concatUniq(previous, prefClasspath);
	}

	private String[] addPreferenceJvmToBootClasspath(String[] previous) {
		String[] prefBootClasspath = cutString("", ";");
		return concatUniq(previous, prefBootClasspath);
	}
}
